<?php

namespace common\helpers;
use Yii;

/*
 *  普通短信发送
        用户在遵循HTTP或者HTTPS协议的前提下，可通过GET和POST方式提交短信发送请求

    短信提交地址
        短信可以提交不超过50000个手机号码，每个号码用英文逗号间隔。(一次提交发送超过200个手机号码，请使用POST请求)

    http://sms.253.com/msg/send 或 https://sms.253.com/msg/send（安全）
 *
 */

class Message
{
    const ACCOUNT = 'N6565631';  //必填参数。用户账号
    const PASSWORD = 'NMRAGg2U4V6c02';  //必填参数。用户密码
    private static $phone; //必填参数。合法的手机号码，号码间用英文逗号分隔
    private static $msg; //必填参数。短信内容，短信内容长度不能超过536个字符。使用URL方式编码为UTF-8格式。短信内容超过70个字符（企信通是60个字符）时，会被拆分成多条，然后以长短信的格式发送。
    private static $rd = 0;   //必填参数。是否需要状态报告，0表示不需要，1表示需要
    private static $ex;  //可选参数，扩展码，用户定义扩展码,扩展码的长度将直接影响短信上行接收的接收。固需要传扩展码参数时，请提前咨询客服相关设置问题。

    public static $errors;
    const URI = 'http://sms.253.com/msg/send';
    const HTTPS_URI = 'https://sms.253.com/msg/send';

    const TIME_INTERVAL = 60;  //发送短信时间间隔 单位秒


    //短信錯誤信息
    private static $msgCode = [
        0 =>'提交成功',
        101=>'无此用户',
        102=>'密码错误',
        103=>'提交过快（提交速度超过流速限制）',
        104=>'系统忙（因平台侧原因，暂时无法处理提交的短信）',
        105=>'敏感短信（短信内容包含敏感词）',
        106=>'消息长度错（>536或<=0）',
        107=>'包含错误的手机号码',
        108=>'手机号码个数错（群发>50000或<=0）',
        109=>'无发送额度（该用户可用短信数已使用完）',
        110=>'不在发送时间内',
        113=>'extno格式错（非数字或者长度不对）',
        116=>'签名不合法或未带签名（用户必须带签名的前提下）',
        117=>'IP地址认证错,请求调用的IP地址不是系统登记的IP地址',
        118=>'用户没有相应的发送权限（账号被禁止发送）',
        119=>'用户已过期',
        120=>'违反放盗用策略(日发限制) --自定义添加',
        121=>'必填参数。是否需要状态报告，取值true或false',
        122=>'5分钟内相同账号提交相同消息内容过多',
        123=>'发送类型错误',
        124=>'白模板匹配错误',
        125=>'匹配驳回模板，提交失败',
        126=>'审核通过模板匹配错误',
    ];

    const TYPE_SIGNUP = 1;  //发送注册短信验证码
    const TYPE_FORGET = 2;
    const TYPE_CHANGE_PHONE = 3;
    const TYPE_NEW_PHONE = 4;
    const TYPE_MESSAGE = 5;

    //短信模板
    private static $msgTemplate = [
        self::TYPE_SIGNUP => '验证码：{code}，请您在5分钟内填写。如非本人操作，请忽略本短信。',
        self::TYPE_FORGET => '验证码：{code}，请您在5分钟内填写。如非本人操作，请忽略本短信。',
        self::TYPE_CHANGE_PHONE => '验证码：{code}，请您在5分钟内填写。如非本人操作，请忽略本短信。',
        self::TYPE_NEW_PHONE => '验证码：{code}，请您在5分钟内填写。如非本人操作，请忽略本短信。',
        self::TYPE_MESSAGE => "您的通讯录好友 {nick_name} {phone}（邀请码 {invite_code}），邀请您快来\<造梦宝\>抢拍iphone，下载链接：http://www.zaomengbao.com/，注册后填写好友邀请码，即可成为好友，并获得钻石、复活卡奖励。",
    ];

    /**
     * 专门用于发送各种验证码
     * @param string $phone
     * @param string $code
     * @param int $type
     * @return bool
     */
    public static function sendVerificationCode($phone = '',$code = '',$type = 1){

        if(strlen($code) != 6){
            self::log(self::$phone,'验证码错误',$code);
            return false;
        }

        $rs = self::sendMessage($phone,$type,['code' => $code]);

        $rs && self::saveMsg($phone,$code,60 * 5,$type);

        return $rs;
    }

    /**
     * 通用发送短信接口
     * @param $phone
     * @param $type
     * @param array $params
     * @return bool
     */
    public static function sendMessage($phone, $type, $params = [])
    {
        $rs = false;

        if(self::checkPhone($phone) && self::checkTime($phone) && $msg = self::checkMessage($type, $params)){
            $postData = [
                'phone'=> $phone,
                'msg' => $msg
            ];
            $rs = self::parseResult(self::postSend(self::URI,$postData));
        }

        return $rs;
    }

    /**
     * @desc 验证短信验证码是否正确
     * @param $phone
     * @param $code
     * @param int $type
     * @return bool
     */
    public static function checkVerificationCode($phone,$code,$type = 1){
        if(trim($code) == Yii::$app->get('redis')->get("msg:{$type}:{$phone}")){
            Yii::$app->get('redis')->del("msg:{$type}:{$phone}");
            return true;
        }else{
            self::$errors[] = '验证码错误.';
            return false;
        }
    }

    /**
     * 获取发送短信错误信息
     * @return string
     */
    public static function getErrorMsg(){
        if(empty(self::$errors)){
            return '短信发送失败.';
        }else{
            return implode('、',self::$errors);
        }
    }

    /**
     * @desc 保存发送的短信信息 用于短信验证
     * @param string $phone 手机号码
     * @param string $code 验证码
     * @param int $second 保存时间 单位秒
     * @param int $type 类型 暂且只有1 1=>注册发送验证码
     */
    private static function saveMsg($phone,$code,$second = 60 * 5,$type = 1){
        Yii::$app->get('redis')->set("msg:{$type}:{$phone}",trim($code));
        Yii::$app->get('redis')->expire("msg:{$type}:{$phone}",$second);
    }

    /**
     *检查指定手机号是否才发了短信 每次发短信的间隔时间为60秒
     * @param string $phone
     * @return bool
     */
    private static function checkTime($phone = ''){
        $rs = false;
        if(!empty($phone)){
            $rs = Yii::$app->get('redis')->exists('lashMsg:'.trim($phone)) ? false : true;
            !$rs && self::$errors[] = '短信发送太频繁，请稍后在试。';
        }
        return $rs;
    }

    /**
     * @desc 保存最近一次指定手机号发送短信的时间
     * @param string $phone
     */
    private static function setTime($phone = ''){
        Yii::$app->get('redis')->set('lashMsg:'.trim($phone),time());
        Yii::$app->get('redis')->expire('lashMsg:'.trim($phone),60);
    }

    /**
     * @desc 验证手机号码
     * @param string $phone
     * @return bool
     */
    private static function checkPhone($phone  = ''){
        $rs = false;

        if(preg_match("/^1[34578]{1}\d{9}$/",$phone)){
            self::$phone = trim($phone);
            $rs = true;
        }else{
            self::log($phone,'手机号码有误。');
            $rs = false;
        }

        return $rs;
    }
    private static function checkType($type){
        if(in_array($type,array_keys(self::$msgTemplate))){
            return true;
        }else{
            self::log(self::$phone,'类型错误');
        }
    }

    /**
     * @desc 验证发送的内容的变量数据
     * @param string $code 模板需要的数据
     * @param int $type 模板
     * @return bool
     */


    /**
     * 验证短短信内容
     * @param $type
     * @param array $params
     * @return bool
     */
    private static function checkMessage($type,$params = []){
        $message = '';

        $message = self::$msgTemplate[$type]; //参数为空 表示发送固定短信
        foreach ($params as $key => $value){
            $message = str_replace('{'.$key.'}',trim($value),$message);
        }

        return $message;
    }


    /*
     * 记录日志
     */
    private static function log($phone,$msg = '',$code = '',$type = 1){
        !empty($msg) && self::$errors[] = $msg;
    }


    private static function postSend($url, $post_data = [], $timeout = 5,$rd = 1){
        if(empty($post_data)) return false;
        $post_data = array_merge([
            'un' => self::ACCOUNT,
            'pw' => self::PASSWORD,
            'rd' => 1,
        ],$post_data);

        $ch = curl_init();
        curl_setopt ($ch, CURLOPT_URL, $url);
        curl_setopt ($ch, CURLOPT_POST, 1);
        if(!empty($post_data)){
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
        }
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        $result = curl_exec($ch);
        curl_close($ch);
        $result = preg_split("/[,\r\n]/",$result);
        return $result;
    }
    private static function parseResult($result = []){
        $rs = false;

        if(is_array($result) && isset($result[1])){
            if($result[1] == 0){
                $rs = true;
                self::setTime(self::$phone);
            }else{
                self::log(self::$phone,self::$msgCode[intval($result[1])]);
            }
        }
        return $rs;
    }
}